home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / smail-3.1.28 / util / pathmerge.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-05  |  6.7 KB  |  283 lines

  1. /* @(#)util/pathmerge.c    1.4 9/6/92 01:10:35 */
  2. /*
  3.  *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
  4.  *    Copyright (C) 1992  Ronald S. Karr
  5.  * 
  6.  * See the file COPYING, distributed with smail, for restriction
  7.  * and warranty information.
  8.  */
  9.  
  10. /*
  11.  * pathmerge - PATH-FILE MERGING PROGRAM
  12.  *
  13.  *    This program takes a set of sorted path files, as produced
  14.  *    by pathalias, and generates on the standard output a merge
  15.  *    of the path information, with one path given for each
  16.  *    hostname.  Precedence in preferred paths goes to the files
  17.  *    given earliest in the argument list.  One of the filenames
  18.  *    given in the argument list can be `-' for the standard
  19.  *    input.
  20.  */
  21. #include <stdio.h>
  22. #include <ctype.h>
  23. #include "defs.h"
  24.  
  25. extern int errno;        /* last system error */
  26. char *malloc();            /* our storage allocator */
  27. char *program;            /* who we are */
  28.  
  29. #define MAXLINE    (4096+1+1)    /* max line length + newline + NULL */
  30.  
  31. #define TRUE 1
  32. #define FALSE 0
  33.  
  34. #define lower(c) ((char)(lowercase(toascii((int)(c)))))    /* safe tolower */
  35.  
  36. /*
  37.  * file state
  38.  */
  39. enum state {
  40.     eof,            /* the file has reached EOF */
  41.     readme,            /* the file needs to be read */
  42.     ready            /* the file has a line ready */
  43. };
  44.  
  45. main(argc,argv)
  46.     register int argc;
  47.     char *argv[];
  48. {
  49.     char **line;        /* current input line, per file */
  50.     enum state *status;    /* end, readme or ready state, per-file */
  51.     FILE **fd;        /* per-file file descriptors */
  52.     register int i;        /* index */
  53.     int eofs;        /* number of files at EOF */
  54.     char *previous;        /* the previous line written */
  55.     int next;        /* the next line to be written */
  56.  
  57.     /*
  58.      * arg check
  59.      */
  60.     if (argc <= 1) {
  61.         /* no arguments, do nothing */
  62.         exit(0);
  63.     } else {
  64.         program = argv[0]; /* remember who we are */
  65.         ++argv;        /* ignore argv[0] */
  66.         --argc;
  67.     }
  68.  
  69.     /*
  70.      * malloc our arrays
  71.      */
  72.     /* input buffers */
  73.     line  = (char **)malloc((argc+1) * sizeof(char **));
  74.     if (line == NULL) {
  75.         perror(program);
  76.         exit(errno);
  77.     }
  78.     for (i=0; i < argc+1; ++i) { /* line[argc] is also previous */
  79.         line[i] = (char *)malloc((MAXLINE+1) * sizeof(char));
  80.         if (line[i] == NULL) {
  81.             perror(program);
  82.             exit(errno);
  83.         }
  84.         /* note line[i][MAXLINE-1] is initially '\0' */
  85.     }
  86.     previous = line[argc];    /* note previous is an empty string */
  87.     /* file states */
  88.     status  = (enum state *)malloc(argc * sizeof(enum state *));
  89.     if (status == NULL) {
  90.         perror(program);
  91.         exit(errno);
  92.     }
  93.     /* per-file file descriptors */
  94.     fd = (FILE **)malloc(argc * sizeof(FILE **));
  95.     if (fd == NULL) {
  96.         perror(program);
  97.         exit(errno);
  98.     }
  99.  
  100.     /*
  101.      * open our files, assign buffers and eof status
  102.      */
  103.     for (i = 0; i < argc; i++) {
  104.         /* open the file */
  105.         if (strcmp(argv[i], "-") == 0) {
  106.             fd[i] = stdin; /* use standard input */
  107.         } else     if ((fd[i] = fopen(argv[i], "r")) == NULL) {
  108.             fprintf(stderr, "%s: ", program);
  109.             perror(argv[i]);
  110.             exit(errno);
  111.         }
  112.         /* enable the read on this file */
  113.         status[i] = readme;
  114.     }
  115.  
  116.     /*
  117.      * if only one file, pass it thru and exit
  118.      */
  119.     if (argc == 1) {
  120.         /* only one file to merge */
  121.         while ((i = getc(fd[0])) != EOF) {
  122.             putchar(i);
  123.         }
  124.         exit(0);
  125.     }
  126.  
  127.     /*
  128.      * sort the files
  129.      */
  130.     eofs = 0;        /* no files at EOF */
  131.     for (;;) {
  132.         /*
  133.          * read lines on files that need reading
  134.          */
  135.         for (i = 0; i < argc; i++) {
  136.             /* only look at files that need to be read */
  137.             if (status[i] != readme) {
  138.                 continue;
  139.             }
  140.             /* read a line from the file */
  141.             eofs = readline(argv, fd, line, status, eofs, i);
  142.         }
  143.         /* if all files then done, stop */
  144.         if (eofs >= argc) {
  145.             break;
  146.         }
  147.  
  148.         /*
  149.          * find the alphabetically least line
  150.          */
  151.         for (next = -1, i = 0; i < argc; i++) {
  152.             /*
  153.              * only look at lines that are ready
  154.              * if current line is earler than before, note it
  155.              */
  156.             if (status[i] == ready &&
  157.                 ((next < 0) || cmp(line[i], line[next]) < 0)) {
  158.                 next = i; /* our newest early line */
  159.             }
  160.         }
  161.  
  162.         /*
  163.          * if we found a next line, print it
  164.          */
  165.         if (next >= 0) {
  166.             /* write the line if it is different */
  167.             if (cmp(previous, line[next]) != 0) {
  168.                 /* write out that line */
  169.                 fputs(line[next], stdout);
  170.                 /* save it */
  171.                 strncpy(previous, line[next], MAXLINE);
  172.             }
  173.             status[next] = readme; /* ready to read again */
  174.             /* skip lines that are equal to the selected one */
  175.             for (i=next+1; i < argc; ++i) {
  176.                 if (status[i] == ready &&
  177.                     cmp(line[i], previous) == 0) {
  178.                     status[i] = readme;
  179.                 }
  180.             }
  181.         }
  182.     }
  183.  
  184.     /*
  185.      * all done
  186.      */
  187.     exit(0);
  188. }
  189.  
  190. /*
  191.  * readline - read a line from a file and check for EOFs and long lines
  192.  *
  193.  * returns the new EOFs count
  194.  */
  195. int
  196. readline(name, fd, line, status, eofs, which)
  197.     char *name[];        /* the names of the files */
  198.     FILE **fd;        /* per-file file descriptors */
  199.     char **line;        /* current input line, per file */
  200.     enum state *status;    /* end, readme or ready state, per-file */
  201.     int eofs;        /* number of files on EOF state */
  202.     int which;        /* which file is being read */
  203. {
  204.     int i;            /* char buffer */
  205.  
  206.     /*
  207.      * read the line
  208.      *
  209.      * If the line is MAXLINE-2 chars + newline, then line[MAXLINE-1]
  210.      * will be '\0'.  Any line that is longer will alter line[MAXLINE-1].
  211.      */
  212.     if (fgets(line[which], MAXLINE+1, fd[which]) != NULL) {
  213.         /*
  214.          * we got a line, is it a good one?
  215.          */
  216.         if (line[which][MAXLINE-1] != '\0') {
  217.             /* the line was too long, skip it */
  218.             fprintf(stderr, "%s: %s line too long\n",
  219.                 program, name[which]);
  220.             while ((i = getc(fd[which])) != '\n') {
  221.                 /* watch for EOF */
  222.                 if (i == EOF) {
  223.                     status[which] = eof;
  224.                     return(eofs+1); /* nothing left */
  225.                 }
  226.             }
  227.             /* clear for next read */
  228.             line[which][MAXLINE-1] = '\0';
  229.             status[which] = readme; /* try again later */
  230.         } else {
  231.             /* we have a line to use */
  232.             status[which] = ready;
  233.         }
  234.     } else {
  235.         /* we read the end of the file */
  236.         status[which] = eof;
  237.         ++eofs;
  238.     }
  239.     return(eofs);        /* return EOF count */
  240. }
  241.     
  242. /*
  243.  * cmp - compare two pathalias lines
  244.  *
  245.  * compare two strings up to the first tab (or space) character
  246.  * to determine if the first is less than, equal to,
  247.  * or greater than the second, returning -1, 0 and 1
  248.  * respectively.
  249.  */
  250. int
  251. cmp(s, t)
  252.     register char *s;    /* first line */
  253.     register char *t;    /* second line */
  254. {
  255.     /*
  256.      * look for an irregularity
  257.      */
  258.     while (*s == *t && !isspace(*s) && *s != '\0' && *s != ':') {
  259.         ++s;
  260.         ++t;
  261.     }
  262.     /* we have stopped at the end of the string, or on the tab */
  263.  
  264.     /*
  265.      * determine the relationship between the lines
  266.      */
  267.     /* if we matched all the way, then note a line match */
  268.     if ((isspace(*s) || *s == ':' || *s == '\0') &&
  269.         (isspace(*t) || *t == ':' || *t == '\0'))
  270.     {
  271.         return (0);
  272.     /* if the first line ended early, it is before the second */
  273.     } else if (isspace(*s) || *s == '\0' || *s == ':') {
  274.         return (-1);
  275.     /* if the second line ended early, it is before the first */
  276.     } else if (isspace(*t) || *t == '\0' || *t == ':') {
  277.         return (1);
  278.     /* we hit a difference spot, note which is earler */
  279.     } else {
  280.         return ((lower(*s) < lower(*t)) ? -1 : 1);
  281.     }
  282. }
  283.